R Packages

packages <- c("tidyverse", "eepR", "furrr", "ggpubr")
xfun::pkg_attach(packages, install = T, message = F)

script_dir <- "~/Documents/GitHub/ppi/R/"
script_files <- list.files(script_dir)
script_path <- paste0(script_dir, script_files)
script_ppi <- lapply(script_path, source)

Set Parameters

tr <- 1.5
n_volumes <- 260
upsample_factor <- 16

Ideal HRF

hrf <- get_hrf_afni("spmg1", tr, upsample_factor)
## 3dDeconvolve -nodata 400 0.09375 -polort -1 -num_stimts 1 -stim_times 1 1D:0 SPMG1 -x1D /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/fileb21944cb923d -x1D_stop
ggplot(hrf %>% mutate(volume = row_number()), aes(volume, hrf)) +
  geom_line() +
  theme_minimal() +
  labs(title = "HRF: SPMG1",
       subtitle = "Upsample: ", upsample_factor)

Psychological Variables

Label Volume by Trial Type

df_psy <- read.csv("~/Box/my_mri/behavioral/nback_3tb4188_2017_Sep_13_1341_soa.csv") %>%
  rename(onset = start, trial_type = block) %>%
  mutate(duration = ifelse(trial_type == "0-back", 10 * 2.5, 20 * 2.5)) %>%
  group_by(run) %>%
  nest() %>%
  mutate(data = future_map(data, function(x) x %>% select(onset, duration, trial_type)),
         data_trial_type = future_pmap(list(data, tr, n_volumes),  create_trial_type_by_volume_list)) %>%
  unnest(data_trial_type) %>%
  select(-data) %>%
  ungroup()
head(df_psy)
## # A tibble: 6 x 3
##     run volume trial_type
##   <int>  <int> <fct>     
## 1     1      1 fixation  
## 2     1      2 fixation  
## 3     1      3 fixation  
## 4     1      4 3-back    
## 5     1      5 3-back    
## 6     1      6 3-back

Contrast Code

func_fig_contrast <- function(data) {
  data %>%
    as.data.frame() %>%
    mutate(volume = row_number()) %>%
    gather(., psy_contrast, value, -volume) %>%
    ggplot(., aes(volume, value)) +
    geom_line() +
    facet_wrap(~ psy_contrast) +
    theme_minimal()
}

func_add_fig_title <- function(fig, text) {
  fig + 
    labs(title = text)
}

df_psy_contrast <- contrast_code_categorical_variable(df_psy, cbind(nback_vs_baseline = c(1, 1, 1, 1, -4)/5,
                                              task_vs_control = c(-3, 1, 1, 1, 0)/4,
                                              linear_task = c(0, -1, 0, 1, 0),
                                              quadratic_task = c(0, -1, 2, -1, 0)/3)) %>%
  group_by(run) %>%
  nest() %>%
  mutate(data = future_map(data, function(x) x %>% select(contains("psy"))),
         fig = future_map(data, func_fig_contrast),
         fig = future_map2(fig, paste0("Run: ", run), func_add_fig_title))

ggarrange(plotlist = df_psy_contrast$fig, ncol = 1)

Upsample

df_psy_upsample <- df_psy_contrast %>%
  mutate(data_upsample = future_map(data, function(x) apply(x, 2, function(x) upsample(x, upsample_factor))),
         fig_upsample = future_map(data_upsample, func_fig_contrast),
         fig_upsample = future_map2(fig_upsample, paste0("Run: ", run), func_add_fig_title))

ggarrange(plotlist = df_psy_upsample$fig_upsample, ncol = 1)

Convolve

df_psy_convolve <- df_psy_upsample %>%
  mutate(data_convolve = future_map(data_upsample, function(x) apply(x, 2, function(x) convolve_afni(x, hrf, tr, n_volumes, upsample_factor))),
         fig_convolve = future_map(data_convolve, func_fig_contrast),
         fig_convolve = future_map2(fig_convolve, paste0("Run:", run), func_add_fig_title))
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160
ggarrange(plotlist = df_psy_convolve$fig_convolve, ncol = 1)

Downsample

df_psy_downsample <- df_psy_convolve %>%
  mutate(data_downsample = future_map(data_convolve, function(x) apply(x, 2, function(x) downsample(x, upsample_factor))),
         fig_downsample = future_map(data_downsample, func_fig_contrast),
         fig_downsample = future_map2(fig_downsample, paste0("Run:", run), func_add_fig_title))


ggarrange(plotlist = df_psy_downsample$fig_downsample, ncol = 1)

Physiological Variable

Create Sphere(s)

This step can be skipped if have you have your own mask.

Extract ROI

func_fig_phys <- function(data) {
  colnames(data) <- "data"
  
  data <- as.data.frame(data)
  
  fig <- data %>%
    mutate(volume = row_number()) %>%
    ggplot(., aes(volume, data)) +
    geom_line() +
    theme_minimal()
  return(fig)
}

dir_phys <- "/Volumes/shared/KK_KR_JLBS/Wave1/MRI/FMRI/PPI/Nback_individual_covariates/PHYS_MNI_42_-42_42/3tb1780/"
files_phys <- list.files(dir_phys, "_mean.1D")
path_phys <- paste0(dir_phys, files_phys)
df_phys <- tibble(file = files_phys,
                  path = path_phys) %>%
  mutate(run = str_remove(file, "_mean.1D"),
         run = str_remove(run, "r"),
         data = future_map(path, function(x) read.csv(x, header = F, col.names = "data")),
         fig = future_map(data, func_fig_phys),
         fig = future_map2(fig, paste0("Run: ", run), func_add_fig_title))

ggarrange(plotlist = df_phys$fig, ncol = 1)

Detrend

df_phys_detrend <- df_phys %>%
  mutate(data_detrend = future_map(data, function(x) detrend(x, 2)),
         fig_detrend = future_map(data_detrend, func_fig_phys),
         fig_detrend = future_map2(fig_detrend, paste0("Run: ", run), func_add_fig_title))


ggarrange(plotlist = df_phys_detrend$fig_detrend, ncol = 1)

Upsample

df_phys_upsample <- df_phys_detrend %>%
  mutate(data_upsample = future_map(data_detrend, function(x) apply(x, 2, function(x) upsample(x, upsample_factor))),
         fig_upsample = future_map(data_upsample, func_fig_phys),
         fig_upsample = future_map2(fig_upsample, paste0("Run: ", run), func_add_fig_title))

ggarrange(plotlist = df_phys_upsample$fig_upsample, ncol = 1)

Deconvolve

df_phys_deconvolve <- df_phys_upsample %>%
  mutate(data_deconvolve = future_map(data_upsample, function(x) apply(x, 2, function(x) deconvolve_afni(x, hrf))),
         fig_deconvolve = future_map(data_deconvolve, func_fig_phys),
         fig_deconvolve = future_map2(fig_deconvolve, paste0("Run:", run), func_add_fig_title))
## 3dTfitter -RHS /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_deconvolve_afni//data.1D -FALTUNG /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_deconvolve_afni//hrf.1D /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_deconvolve_afni//data_deconvolved.1D 012 -2 -l2lasso -6 
## 3dTfitter -RHS /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_deconvolve_afni//data.1D -FALTUNG /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_deconvolve_afni//hrf.1D /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_deconvolve_afni//data_deconvolved.1D 012 -2 -l2lasso -6 
## 3dTfitter -RHS /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_deconvolve_afni//data.1D -FALTUNG /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_deconvolve_afni//hrf.1D /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_deconvolve_afni//data_deconvolved.1D 012 -2 -l2lasso -6
ggarrange(plotlist = df_phys_deconvolve$fig_deconvolve, ncol = 1)

Psychophysiological Interaction Variable

Multiply/Create Interaction

df_ppi <- tibble(run = c(1:3),
                 data = NA)

for (i in 1:nrow(df_ppi)) {
  df_ppi$data[i] <- list(apply(df_psy_upsample$data_upsample[[i]], 2, function(x) x * df_phys_deconvolve$data_deconvolve[[i]]))
}

df_ppi <- df_ppi %>%
  mutate(fig = future_map(data, func_fig_contrast),
         fig = future_map2(fig, paste0("Run:", run), func_add_fig_title))

ggarrange(plotlist = df_ppi$fig, ncol = 1)

Convolve

df_ppi_convolve <- df_ppi %>%
  mutate(data_convolve = future_map(data, function(x) apply(x, 2, function(x) convolve_afni(x, hrf, tr, n_volumes, upsample_factor))),
         fig_convolve = future_map(data_convolve, func_fig_contrast),
         fig_convolve = future_map2(fig_convolve, paste0("Run:", run), func_add_fig_title))
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160 
## waver -FILE 0.09375 /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//hrf.1D -input /var/folders/_2/600bt_js7d5dpsn7nbfcpy4x88r01g/T//RtmpaLImsB/temp_convolution_afni//data.1D -numout 4160
ggarrange(plotlist = df_ppi_convolve$fig_convolve, ncol = 1)

Downsample

df_ppi_downsample <- df_ppi_convolve %>%
  mutate(data_downsample = future_map(data_convolve, function(x) apply(x, 2, function(x) downsample(x, upsample_factor))),
         fig_downsample = future_map(data_downsample, func_fig_contrast),
         fig_downsample = future_map2(fig_downsample, paste0("Run:", run), func_add_fig_title))

ggarrange(plotlist = df_ppi_downsample$fig_downsample, ncol = 1)

Design Matrix

Initialize

df_design_mat <- tibble(run = c(1:3),
                        data = NA)

for (i in 1:nrow(df_design_mat)) {
  
  temp_psy <- df_psy_downsample$data_downsample[[i]]
  temp_phys <- df_phys_detrend$data_detrend[[i]] %>% rename(phys = residual)
  temp_ppi <- df_ppi_downsample$data_downsample[[i]]
  colnames(temp_ppi) <- str_replace(colnames(temp_ppi), "psy_", "ppi_")

  df_design_mat$data[i] <- list(cbind(temp_psy, temp_phys, temp_ppi))
}

df_design_mat <- df_design_mat %>%
  unnest()
## Warning: `cols` is now required.
## Please use `cols = c(data)`
func_fig_data_heat_map <- function(data) {
  colnames(data) <- paste0(str_pad(1:ncol(data), 2, "left", "0"), "_", colnames(data))
  apply(data, 2, scale_min_max) %>%
    as.data.frame() %>%
    mutate(volume = row_number()) %>%
    gather(., variable, value, -volume) %>%
    ggplot(., aes(variable, volume, fill = value)) +
    geom_raster() + 
    scale_fill_distiller(palette = "Greys", direction = 1) + 
    theme_minimal() +
    theme(axis.text.x = element_text(hjust = 1, angle = 45)) +
    labs(x = NULL)
}

func_fig_data_ts_long <- function(data) {
  colnames(data) <- paste0(str_pad(1:ncol(data), 2, "left", "0"), "_", colnames(data))
  data %>%
  mutate(volume = row_number()) %>%
  gather(., variable, value, -volume) %>%
  ggplot(., aes(volume, value)) +
  geom_line() +
  facet_wrap(~ variable, scales = "free_y", ncol = 1) +
  theme_minimal()
}

func_fig_data_heat_map(df_design_mat)

func_fig_data_ts_long(df_design_mat)

Add nuisance variables

Final

df_design_mat_final <- df_design_mat %>%
  group_by(run) %>%
  nest() %>%
  mutate(data = future_map(data, function(x) x %>% mutate(time_linear = row_number(), 
                                                          time_linear = scale_min_max(time_linear),
                                                          time_quadratic = time_linear^2))) %>%
  unnest() %>%
  ungroup()
## Warning: `cols` is now required.
## Please use `cols = c(data)`
func_fig_data_heat_map(df_design_mat_final)

func_fig_data_ts_long(df_design_mat_final)

LS0tCnRpdGxlOiAiUFBJIEV4YW1wbGUiCmF1dGhvcjogIkVrYXJpbiBFcmljIFBvbmdwaXBhdCwgTS5BLiIKZGF0ZTogIjIwMTktMDEtMDYiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGhpZ2hsaWdodDogdGV4dG1hdGUKICAgIHRoZW1lOiBsdW1lbgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogeWVzCiAgICAgIHNtb290aF9zY3JvbGw6IHllcwotLS0KCiMjIFIgUGFja2FnZXMKYGBge3IsIHdhcm5pbmcgPSBGfQpwYWNrYWdlcyA8LSBjKCJ0aWR5dmVyc2UiLCAiZWVwUiIsICJmdXJyciIsICJnZ3B1YnIiKQp4ZnVuOjpwa2dfYXR0YWNoKHBhY2thZ2VzLCBpbnN0YWxsID0gVCwgbWVzc2FnZSA9IEYpCgpzY3JpcHRfZGlyIDwtICJ+L0RvY3VtZW50cy9HaXRIdWIvcHBpL1IvIgpzY3JpcHRfZmlsZXMgPC0gbGlzdC5maWxlcyhzY3JpcHRfZGlyKQpzY3JpcHRfcGF0aCA8LSBwYXN0ZTAoc2NyaXB0X2Rpciwgc2NyaXB0X2ZpbGVzKQpzY3JpcHRfcHBpIDwtIGxhcHBseShzY3JpcHRfcGF0aCwgc291cmNlKQpgYGAKCiMjIFNldCBQYXJhbWV0ZXJzCmBgYHtyfQp0ciA8LSAxLjUKbl92b2x1bWVzIDwtIDI2MAp1cHNhbXBsZV9mYWN0b3IgPC0gMTYKYGBgCgojIyBJZGVhbCBIUkYKYGBge3IsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0KaHJmIDwtIGdldF9ocmZfYWZuaSgic3BtZzEiLCB0ciwgdXBzYW1wbGVfZmFjdG9yKQoKZ2dwbG90KGhyZiAlPiUgbXV0YXRlKHZvbHVtZSA9IHJvd19udW1iZXIoKSksIGFlcyh2b2x1bWUsIGhyZikpICsKICBnZW9tX2xpbmUoKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gIkhSRjogU1BNRzEiLAogICAgICAgc3VidGl0bGUgPSAiVXBzYW1wbGU6ICIsIHVwc2FtcGxlX2ZhY3RvcikKYGBgCgojIyBQc3ljaG9sb2dpY2FsIFZhcmlhYmxlcwoKIyMjIExhYmVsIFZvbHVtZSBieSBUcmlhbCBUeXBlCmBgYHtyfQpkZl9wc3kgPC0gcmVhZC5jc3YoIn4vQm94L215X21yaS9iZWhhdmlvcmFsL25iYWNrXzN0YjQxODhfMjAxN19TZXBfMTNfMTM0MV9zb2EuY3N2IikgJT4lCiAgcmVuYW1lKG9uc2V0ID0gc3RhcnQsIHRyaWFsX3R5cGUgPSBibG9jaykgJT4lCiAgbXV0YXRlKGR1cmF0aW9uID0gaWZlbHNlKHRyaWFsX3R5cGUgPT0gIjAtYmFjayIsIDEwICogMi41LCAyMCAqIDIuNSkpICU+JQogIGdyb3VwX2J5KHJ1bikgJT4lCiAgbmVzdCgpICU+JQogIG11dGF0ZShkYXRhID0gZnV0dXJlX21hcChkYXRhLCBmdW5jdGlvbih4KSB4ICU+JSBzZWxlY3Qob25zZXQsIGR1cmF0aW9uLCB0cmlhbF90eXBlKSksCiAgICAgICAgIGRhdGFfdHJpYWxfdHlwZSA9IGZ1dHVyZV9wbWFwKGxpc3QoZGF0YSwgdHIsIG5fdm9sdW1lcyksICBjcmVhdGVfdHJpYWxfdHlwZV9ieV92b2x1bWVfbGlzdCkpICU+JQogIHVubmVzdChkYXRhX3RyaWFsX3R5cGUpICU+JQogIHNlbGVjdCgtZGF0YSkgJT4lCiAgdW5ncm91cCgpCmhlYWQoZGZfcHN5KQpgYGAKCiMjIyBDb250cmFzdCBDb2RlCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyfQpmdW5jX2ZpZ19jb250cmFzdCA8LSBmdW5jdGlvbihkYXRhKSB7CiAgZGF0YSAlPiUKICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUKICAgIG11dGF0ZSh2b2x1bWUgPSByb3dfbnVtYmVyKCkpICU+JQogICAgZ2F0aGVyKC4sIHBzeV9jb250cmFzdCwgdmFsdWUsIC12b2x1bWUpICU+JQogICAgZ2dwbG90KC4sIGFlcyh2b2x1bWUsIHZhbHVlKSkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZmFjZXRfd3JhcCh+IHBzeV9jb250cmFzdCkgKwogICAgdGhlbWVfbWluaW1hbCgpCn0KCmZ1bmNfYWRkX2ZpZ190aXRsZSA8LSBmdW5jdGlvbihmaWcsIHRleHQpIHsKICBmaWcgKyAKICAgIGxhYnModGl0bGUgPSB0ZXh0KQp9CgpkZl9wc3lfY29udHJhc3QgPC0gY29udHJhc3RfY29kZV9jYXRlZ29yaWNhbF92YXJpYWJsZShkZl9wc3ksIGNiaW5kKG5iYWNrX3ZzX2Jhc2VsaW5lID0gYygxLCAxLCAxLCAxLCAtNCkvNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhc2tfdnNfY29udHJvbCA9IGMoLTMsIDEsIDEsIDEsIDApLzQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5lYXJfdGFzayA9IGMoMCwgLTEsIDAsIDEsIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhZHJhdGljX3Rhc2sgPSBjKDAsIC0xLCAyLCAtMSwgMCkvMykpICU+JQogIGdyb3VwX2J5KHJ1bikgJT4lCiAgbmVzdCgpICU+JQogIG11dGF0ZShkYXRhID0gZnV0dXJlX21hcChkYXRhLCBmdW5jdGlvbih4KSB4ICU+JSBzZWxlY3QoY29udGFpbnMoInBzeSIpKSksCiAgICAgICAgIGZpZyA9IGZ1dHVyZV9tYXAoZGF0YSwgZnVuY19maWdfY29udHJhc3QpLAogICAgICAgICBmaWcgPSBmdXR1cmVfbWFwMihmaWcsIHBhc3RlMCgiUnVuOiAiLCBydW4pLCBmdW5jX2FkZF9maWdfdGl0bGUpKQoKZ2dhcnJhbmdlKHBsb3RsaXN0ID0gZGZfcHN5X2NvbnRyYXN0JGZpZywgbmNvbCA9IDEpCmBgYAoKIyMjIFVwc2FtcGxlCmBgYHtyLCBtZXNzYWdlID0gRiwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KZGZfcHN5X3Vwc2FtcGxlIDwtIGRmX3BzeV9jb250cmFzdCAlPiUKICBtdXRhdGUoZGF0YV91cHNhbXBsZSA9IGZ1dHVyZV9tYXAoZGF0YSwgZnVuY3Rpb24oeCkgYXBwbHkoeCwgMiwgZnVuY3Rpb24oeCkgdXBzYW1wbGUoeCwgdXBzYW1wbGVfZmFjdG9yKSkpLAogICAgICAgICBmaWdfdXBzYW1wbGUgPSBmdXR1cmVfbWFwKGRhdGFfdXBzYW1wbGUsIGZ1bmNfZmlnX2NvbnRyYXN0KSwKICAgICAgICAgZmlnX3Vwc2FtcGxlID0gZnV0dXJlX21hcDIoZmlnX3Vwc2FtcGxlLCBwYXN0ZTAoIlJ1bjogIiwgcnVuKSwgZnVuY19hZGRfZmlnX3RpdGxlKSkKCmdnYXJyYW5nZShwbG90bGlzdCA9IGRmX3BzeV91cHNhbXBsZSRmaWdfdXBzYW1wbGUsIG5jb2wgPSAxKQpgYGAKCiMjIyBDb252b2x2ZQpgYGB7ciwgbWVzc2FnZSA9IEYsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CmRmX3BzeV9jb252b2x2ZSA8LSBkZl9wc3lfdXBzYW1wbGUgJT4lCiAgbXV0YXRlKGRhdGFfY29udm9sdmUgPSBmdXR1cmVfbWFwKGRhdGFfdXBzYW1wbGUsIGZ1bmN0aW9uKHgpIGFwcGx5KHgsIDIsIGZ1bmN0aW9uKHgpIGNvbnZvbHZlX2FmbmkoeCwgaHJmLCB0ciwgbl92b2x1bWVzLCB1cHNhbXBsZV9mYWN0b3IpKSksCiAgICAgICAgIGZpZ19jb252b2x2ZSA9IGZ1dHVyZV9tYXAoZGF0YV9jb252b2x2ZSwgZnVuY19maWdfY29udHJhc3QpLAogICAgICAgICBmaWdfY29udm9sdmUgPSBmdXR1cmVfbWFwMihmaWdfY29udm9sdmUsIHBhc3RlMCgiUnVuOiIsIHJ1biksIGZ1bmNfYWRkX2ZpZ190aXRsZSkpCgoKZ2dhcnJhbmdlKHBsb3RsaXN0ID0gZGZfcHN5X2NvbnZvbHZlJGZpZ19jb252b2x2ZSwgbmNvbCA9IDEpCmBgYAoKIyMjIERvd25zYW1wbGUKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CmRmX3BzeV9kb3duc2FtcGxlIDwtIGRmX3BzeV9jb252b2x2ZSAlPiUKICBtdXRhdGUoZGF0YV9kb3duc2FtcGxlID0gZnV0dXJlX21hcChkYXRhX2NvbnZvbHZlLCBmdW5jdGlvbih4KSBhcHBseSh4LCAyLCBmdW5jdGlvbih4KSBkb3duc2FtcGxlKHgsIHVwc2FtcGxlX2ZhY3RvcikpKSwKICAgICAgICAgZmlnX2Rvd25zYW1wbGUgPSBmdXR1cmVfbWFwKGRhdGFfZG93bnNhbXBsZSwgZnVuY19maWdfY29udHJhc3QpLAogICAgICAgICBmaWdfZG93bnNhbXBsZSA9IGZ1dHVyZV9tYXAyKGZpZ19kb3duc2FtcGxlLCBwYXN0ZTAoIlJ1bjoiLCBydW4pLCBmdW5jX2FkZF9maWdfdGl0bGUpKQoKCmdnYXJyYW5nZShwbG90bGlzdCA9IGRmX3BzeV9kb3duc2FtcGxlJGZpZ19kb3duc2FtcGxlLCBuY29sID0gMSkKYGBgCgojIyBQaHlzaW9sb2dpY2FsIFZhcmlhYmxlCgojIyMgQ3JlYXRlIFNwaGVyZShzKQoKVGhpcyBzdGVwIGNhbiBiZSBza2lwcGVkIGlmIGhhdmUgeW91IGhhdmUgeW91ciBvd24gbWFzay4gCgpgYGB7cn0KCmBgYAoKIyMjIEV4dHJhY3QgUk9JCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDZ9CmZ1bmNfZmlnX3BoeXMgPC0gZnVuY3Rpb24oZGF0YSkgewogIGNvbG5hbWVzKGRhdGEpIDwtICJkYXRhIgogIAogIGRhdGEgPC0gYXMuZGF0YS5mcmFtZShkYXRhKQogIAogIGZpZyA8LSBkYXRhICU+JQogICAgbXV0YXRlKHZvbHVtZSA9IHJvd19udW1iZXIoKSkgJT4lCiAgICBnZ3Bsb3QoLiwgYWVzKHZvbHVtZSwgZGF0YSkpICsKICAgIGdlb21fbGluZSgpICsKICAgIHRoZW1lX21pbmltYWwoKQogIHJldHVybihmaWcpCn0KCmRpcl9waHlzIDwtICIvVm9sdW1lcy9zaGFyZWQvS0tfS1JfSkxCUy9XYXZlMS9NUkkvRk1SSS9QUEkvTmJhY2tfaW5kaXZpZHVhbF9jb3ZhcmlhdGVzL1BIWVNfTU5JXzQyXy00Ml80Mi8zdGIxNzgwLyIKZmlsZXNfcGh5cyA8LSBsaXN0LmZpbGVzKGRpcl9waHlzLCAiX21lYW4uMUQiKQpwYXRoX3BoeXMgPC0gcGFzdGUwKGRpcl9waHlzLCBmaWxlc19waHlzKQpkZl9waHlzIDwtIHRpYmJsZShmaWxlID0gZmlsZXNfcGh5cywKICAgICAgICAgICAgICAgICAgcGF0aCA9IHBhdGhfcGh5cykgJT4lCiAgbXV0YXRlKHJ1biA9IHN0cl9yZW1vdmUoZmlsZSwgIl9tZWFuLjFEIiksCiAgICAgICAgIHJ1biA9IHN0cl9yZW1vdmUocnVuLCAiciIpLAogICAgICAgICBkYXRhID0gZnV0dXJlX21hcChwYXRoLCBmdW5jdGlvbih4KSByZWFkLmNzdih4LCBoZWFkZXIgPSBGLCBjb2wubmFtZXMgPSAiZGF0YSIpKSwKICAgICAgICAgZmlnID0gZnV0dXJlX21hcChkYXRhLCBmdW5jX2ZpZ19waHlzKSwKICAgICAgICAgZmlnID0gZnV0dXJlX21hcDIoZmlnLCBwYXN0ZTAoIlJ1bjogIiwgcnVuKSwgZnVuY19hZGRfZmlnX3RpdGxlKSkKCmdnYXJyYW5nZShwbG90bGlzdCA9IGRmX3BoeXMkZmlnLCBuY29sID0gMSkKYGBgCgojIyMgRGV0cmVuZApgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQpkZl9waHlzX2RldHJlbmQgPC0gZGZfcGh5cyAlPiUKICBtdXRhdGUoZGF0YV9kZXRyZW5kID0gZnV0dXJlX21hcChkYXRhLCBmdW5jdGlvbih4KSBkZXRyZW5kKHgsIDIpKSwKICAgICAgICAgZmlnX2RldHJlbmQgPSBmdXR1cmVfbWFwKGRhdGFfZGV0cmVuZCwgZnVuY19maWdfcGh5cyksCiAgICAgICAgIGZpZ19kZXRyZW5kID0gZnV0dXJlX21hcDIoZmlnX2RldHJlbmQsIHBhc3RlMCgiUnVuOiAiLCBydW4pLCBmdW5jX2FkZF9maWdfdGl0bGUpKQoKCmdnYXJyYW5nZShwbG90bGlzdCA9IGRmX3BoeXNfZGV0cmVuZCRmaWdfZGV0cmVuZCwgbmNvbCA9IDEpCmBgYAoKIyMjIFVwc2FtcGxlCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDZ9CmRmX3BoeXNfdXBzYW1wbGUgPC0gZGZfcGh5c19kZXRyZW5kICU+JQogIG11dGF0ZShkYXRhX3Vwc2FtcGxlID0gZnV0dXJlX21hcChkYXRhX2RldHJlbmQsIGZ1bmN0aW9uKHgpIGFwcGx5KHgsIDIsIGZ1bmN0aW9uKHgpIHVwc2FtcGxlKHgsIHVwc2FtcGxlX2ZhY3RvcikpKSwKICAgICAgICAgZmlnX3Vwc2FtcGxlID0gZnV0dXJlX21hcChkYXRhX3Vwc2FtcGxlLCBmdW5jX2ZpZ19waHlzKSwKICAgICAgICAgZmlnX3Vwc2FtcGxlID0gZnV0dXJlX21hcDIoZmlnX3Vwc2FtcGxlLCBwYXN0ZTAoIlJ1bjogIiwgcnVuKSwgZnVuY19hZGRfZmlnX3RpdGxlKSkKCmdnYXJyYW5nZShwbG90bGlzdCA9IGRmX3BoeXNfdXBzYW1wbGUkZmlnX3Vwc2FtcGxlLCBuY29sID0gMSkKYGBgCgojIyMgRGVjb252b2x2ZQpgYGB7ciwgbWVzc2FnZSA9IEYsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNn0KZGZfcGh5c19kZWNvbnZvbHZlIDwtIGRmX3BoeXNfdXBzYW1wbGUgJT4lCiAgbXV0YXRlKGRhdGFfZGVjb252b2x2ZSA9IGZ1dHVyZV9tYXAoZGF0YV91cHNhbXBsZSwgZnVuY3Rpb24oeCkgYXBwbHkoeCwgMiwgZnVuY3Rpb24oeCkgZGVjb252b2x2ZV9hZm5pKHgsIGhyZikpKSwKICAgICAgICAgZmlnX2RlY29udm9sdmUgPSBmdXR1cmVfbWFwKGRhdGFfZGVjb252b2x2ZSwgZnVuY19maWdfcGh5cyksCiAgICAgICAgIGZpZ19kZWNvbnZvbHZlID0gZnV0dXJlX21hcDIoZmlnX2RlY29udm9sdmUsIHBhc3RlMCgiUnVuOiIsIHJ1biksIGZ1bmNfYWRkX2ZpZ190aXRsZSkpCgpnZ2FycmFuZ2UocGxvdGxpc3QgPSBkZl9waHlzX2RlY29udm9sdmUkZmlnX2RlY29udm9sdmUsIG5jb2wgPSAxKQpgYGAKCiMjIFBzeWNob3BoeXNpb2xvZ2ljYWwgSW50ZXJhY3Rpb24gVmFyaWFibGUKCiMjIyBNdWx0aXBseS9DcmVhdGUgSW50ZXJhY3Rpb24KYGBge3IsICwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KZGZfcHBpIDwtIHRpYmJsZShydW4gPSBjKDE6MyksCiAgICAgICAgICAgICAgICAgZGF0YSA9IE5BKQoKZm9yIChpIGluIDE6bnJvdyhkZl9wcGkpKSB7CiAgZGZfcHBpJGRhdGFbaV0gPC0gbGlzdChhcHBseShkZl9wc3lfdXBzYW1wbGUkZGF0YV91cHNhbXBsZVtbaV1dLCAyLCBmdW5jdGlvbih4KSB4ICogZGZfcGh5c19kZWNvbnZvbHZlJGRhdGFfZGVjb252b2x2ZVtbaV1dKSkKfQoKZGZfcHBpIDwtIGRmX3BwaSAlPiUKICBtdXRhdGUoZmlnID0gZnV0dXJlX21hcChkYXRhLCBmdW5jX2ZpZ19jb250cmFzdCksCiAgICAgICAgIGZpZyA9IGZ1dHVyZV9tYXAyKGZpZywgcGFzdGUwKCJSdW46IiwgcnVuKSwgZnVuY19hZGRfZmlnX3RpdGxlKSkKCmdnYXJyYW5nZShwbG90bGlzdCA9IGRmX3BwaSRmaWcsIG5jb2wgPSAxKQpgYGAKCiMjIyBDb252b2x2ZQpgYGB7ciwgbWVzc2FnZSA9IEYsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CmRmX3BwaV9jb252b2x2ZSA8LSBkZl9wcGkgJT4lCiAgbXV0YXRlKGRhdGFfY29udm9sdmUgPSBmdXR1cmVfbWFwKGRhdGEsIGZ1bmN0aW9uKHgpIGFwcGx5KHgsIDIsIGZ1bmN0aW9uKHgpIGNvbnZvbHZlX2FmbmkoeCwgaHJmLCB0ciwgbl92b2x1bWVzLCB1cHNhbXBsZV9mYWN0b3IpKSksCiAgICAgICAgIGZpZ19jb252b2x2ZSA9IGZ1dHVyZV9tYXAoZGF0YV9jb252b2x2ZSwgZnVuY19maWdfY29udHJhc3QpLAogICAgICAgICBmaWdfY29udm9sdmUgPSBmdXR1cmVfbWFwMihmaWdfY29udm9sdmUsIHBhc3RlMCgiUnVuOiIsIHJ1biksIGZ1bmNfYWRkX2ZpZ190aXRsZSkpCgpnZ2FycmFuZ2UocGxvdGxpc3QgPSBkZl9wcGlfY29udm9sdmUkZmlnX2NvbnZvbHZlLCBuY29sID0gMSkKYGBgCgojIyMgRG93bnNhbXBsZQpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KZGZfcHBpX2Rvd25zYW1wbGUgPC0gZGZfcHBpX2NvbnZvbHZlICU+JQogIG11dGF0ZShkYXRhX2Rvd25zYW1wbGUgPSBmdXR1cmVfbWFwKGRhdGFfY29udm9sdmUsIGZ1bmN0aW9uKHgpIGFwcGx5KHgsIDIsIGZ1bmN0aW9uKHgpIGRvd25zYW1wbGUoeCwgdXBzYW1wbGVfZmFjdG9yKSkpLAogICAgICAgICBmaWdfZG93bnNhbXBsZSA9IGZ1dHVyZV9tYXAoZGF0YV9kb3duc2FtcGxlLCBmdW5jX2ZpZ19jb250cmFzdCksCiAgICAgICAgIGZpZ19kb3duc2FtcGxlID0gZnV0dXJlX21hcDIoZmlnX2Rvd25zYW1wbGUsIHBhc3RlMCgiUnVuOiIsIHJ1biksIGZ1bmNfYWRkX2ZpZ190aXRsZSkpCgpnZ2FycmFuZ2UocGxvdGxpc3QgPSBkZl9wcGlfZG93bnNhbXBsZSRmaWdfZG93bnNhbXBsZSwgbmNvbCA9IDEpCmBgYAoKIyMgRGVzaWduIE1hdHJpeAoKIyMjIEluaXRpYWxpemUKYGBge3J9CmRmX2Rlc2lnbl9tYXQgPC0gdGliYmxlKHJ1biA9IGMoMTozKSwKICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IE5BKQoKZm9yIChpIGluIDE6bnJvdyhkZl9kZXNpZ25fbWF0KSkgewogIAogIHRlbXBfcHN5IDwtIGRmX3BzeV9kb3duc2FtcGxlJGRhdGFfZG93bnNhbXBsZVtbaV1dCiAgdGVtcF9waHlzIDwtIGRmX3BoeXNfZGV0cmVuZCRkYXRhX2RldHJlbmRbW2ldXSAlPiUgcmVuYW1lKHBoeXMgPSByZXNpZHVhbCkKICB0ZW1wX3BwaSA8LSBkZl9wcGlfZG93bnNhbXBsZSRkYXRhX2Rvd25zYW1wbGVbW2ldXQogIGNvbG5hbWVzKHRlbXBfcHBpKSA8LSBzdHJfcmVwbGFjZShjb2xuYW1lcyh0ZW1wX3BwaSksICJwc3lfIiwgInBwaV8iKQoKICBkZl9kZXNpZ25fbWF0JGRhdGFbaV0gPC0gbGlzdChjYmluZCh0ZW1wX3BzeSwgdGVtcF9waHlzLCB0ZW1wX3BwaSkpCn0KCmRmX2Rlc2lnbl9tYXQgPC0gZGZfZGVzaWduX21hdCAlPiUKICB1bm5lc3QoKQoKZnVuY19maWdfZGF0YV9oZWF0X21hcCA8LSBmdW5jdGlvbihkYXRhKSB7CiAgY29sbmFtZXMoZGF0YSkgPC0gcGFzdGUwKHN0cl9wYWQoMTpuY29sKGRhdGEpLCAyLCAibGVmdCIsICIwIiksICJfIiwgY29sbmFtZXMoZGF0YSkpCiAgYXBwbHkoZGF0YSwgMiwgc2NhbGVfbWluX21heCkgJT4lCiAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICBtdXRhdGUodm9sdW1lID0gcm93X251bWJlcigpKSAlPiUKICAgIGdhdGhlciguLCB2YXJpYWJsZSwgdmFsdWUsIC12b2x1bWUpICU+JQogICAgZ2dwbG90KC4sIGFlcyh2YXJpYWJsZSwgdm9sdW1lLCBmaWxsID0gdmFsdWUpKSArCiAgICBnZW9tX3Jhc3RlcigpICsgCiAgICBzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIkdyZXlzIiwgZGlyZWN0aW9uID0gMSkgKyAKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEsIGFuZ2xlID0gNDUpKSArCiAgICBsYWJzKHggPSBOVUxMKQp9CgpmdW5jX2ZpZ19kYXRhX3RzX2xvbmcgPC0gZnVuY3Rpb24oZGF0YSkgewogIGNvbG5hbWVzKGRhdGEpIDwtIHBhc3RlMChzdHJfcGFkKDE6bmNvbChkYXRhKSwgMiwgImxlZnQiLCAiMCIpLCAiXyIsIGNvbG5hbWVzKGRhdGEpKQogIGRhdGEgJT4lCiAgbXV0YXRlKHZvbHVtZSA9IHJvd19udW1iZXIoKSkgJT4lCiAgZ2F0aGVyKC4sIHZhcmlhYmxlLCB2YWx1ZSwgLXZvbHVtZSkgJT4lCiAgZ2dwbG90KC4sIGFlcyh2b2x1bWUsIHZhbHVlKSkgKwogIGdlb21fbGluZSgpICsKICBmYWNldF93cmFwKH4gdmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlX3kiLCBuY29sID0gMSkgKwogIHRoZW1lX21pbmltYWwoKQp9CgpmdW5jX2ZpZ19kYXRhX2hlYXRfbWFwKGRmX2Rlc2lnbl9tYXQpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSAxMn0KZnVuY19maWdfZGF0YV90c19sb25nKGRmX2Rlc2lnbl9tYXQpCmBgYAoKIyMjIEFkZCBudWlzYW5jZSB2YXJpYWJsZXMKCmBgYHtyfQoKYGBgCgojIyMgRmluYWwKCmBgYHtyfQpkZl9kZXNpZ25fbWF0X2ZpbmFsIDwtIGRmX2Rlc2lnbl9tYXQgJT4lCiAgZ3JvdXBfYnkocnVuKSAlPiUKICBuZXN0KCkgJT4lCiAgbXV0YXRlKGRhdGEgPSBmdXR1cmVfbWFwKGRhdGEsIGZ1bmN0aW9uKHgpIHggJT4lIG11dGF0ZSh0aW1lX2xpbmVhciA9IHJvd19udW1iZXIoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lX2xpbmVhciA9IHNjYWxlX21pbl9tYXgodGltZV9saW5lYXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZV9xdWFkcmF0aWMgPSB0aW1lX2xpbmVhcl4yKSkpICU+JQogIHVubmVzdCgpICU+JQogIHVuZ3JvdXAoKQoKZnVuY19maWdfZGF0YV9oZWF0X21hcChkZl9kZXNpZ25fbWF0X2ZpbmFsKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gMTJ9CmZ1bmNfZmlnX2RhdGFfdHNfbG9uZyhkZl9kZXNpZ25fbWF0X2ZpbmFsKQpgYGAK